#  finger_printer.rb
#
#  This file is part of myvnet.
#
#  Copyright (C) 2008  Filipe David Borba Manana,  fdmanana@gmail.com
#
#  myvnet is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  myvnet is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with myvnet.  If not, see <http://www.gnu.org/licenses/>.


module Packet
  
  # Changes fields in packets so that they seem to have been generated 
  # by a specific TCP/IP network stack implementation. The value of some
  # packet fields and how they change, is not specified in the corresponding 
  # protocols' RFCs, so different OSs set different values for these fields
  # and use different algorithms for value generation.
  # 
  # Currenlty this fingerprinter mimics a Linux 2.6 kernel behaviour.
  #
  class FingerPrinter
    
    def initialize
      @next_icmp_ipv4_id = {}      
    end

    
    # === Synopsis
    # 
    #   Packet::FingerPrinter#process(eth_req_packet, eth_reply_packet)
    #
    # === Args
    # 
    # +eth_req_packet+::
    #   An ethernet packet received from a remote host.
    #   
    # +eth_reply_packet+::
    #   The corresponding ethernet reply (to the ethernet request packet) 
    #   packet to fingerprint. *Note*: this parameter is modified inplace.
    #   
    # === Return
    # 
    # A fingerprinted version of the ethernet reply packet, received as a 
    # parameter. Note that it modifies the packet in place.
    #
    # === Description
    #
    # Modifies header fields of the upper layer protocols, encapsulated by
    # the ethernet reply packet, in order to mimic a particular TCP/IP 
    # network stack implementation.
    #
    def process(eth_req_packet, eth_reply_packet)
      
      # ICMP echo request -> reply
      if eth_reply_packet.has_icmp? and eth_req_packet.has_icmp?
        ip_reply = eth_reply_packet.payload
        ip_req = eth_req_packet.payload
        
        if ip_req.payload.type == :echo_request and 
           ip_reply.payload.type == :echo_reply

          handle_ping(ip_req, ip_reply)
        end
      end
      
      eth_reply_packet
    end
    
    
    private
    
    def handle_ping(ip_req, ip_reply)
      id = @next_icmp_ipv4_id[ip_reply.src]
      
      if (not id) or (id > 0xff_fe)
        @next_icmp_ipv4_id[ip_reply.src] = (rand 0x34_af) + 0x1b_3e
        id = @next_icmp_ipv4_id[ip_reply.src]
      end
      
      ip_reply.id = id
      
      ping_req = ip_req.payload
      ping_reply = ip_reply.payload
      
      # nmap CD (ICMP response code) test, IE test category
      # http://nmap.org/book/osdetect-methods.html
      ping_reply.code = ping_req.code
            
      @next_icmp_ipv4_id[ip_reply.src] += 1
    end
    
  end
  
end